package org.apache.hadoop.ipc;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.net.SocketFactory;

import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.retry.RetryPolicy;
import org.apache.hadoop.ipc.Client.ConnectionId;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.token.SecretManager;
import org.apache.hadoop.security.token.TokenIdentifier;

/**
 * RpcEngine是ProtobufRpcEngine和WritableRpcEngine[已过时]的父类. 在hadoop的RPC通讯的体系中扮演着很重要的角色.
 * 因为正常的RPC请求,分为三个部分: 通讯协议, Client端 和Server端.
 *     通讯协议是Client和Server端沟通的桥梁.
 *     在Client是通过RpcEngine.getProxy 方法获取Server的代理对象.
 *     在Server端是通过getServer方法获取Server端的实例.
 * 所以RpcEngine是Hadoop RPC 通讯体系中非常重要的父类定义.
 *
 * 真正构造代理对象的方法其实是RpcEngine.getProxy()方法，这里的RpcEngine是一个抽象类，它只定义了接口，具体的实现留给子类去做。
 * RpcEngine目前有两个子类，
 *     WritableRpcEngine 用于描述使用Hadoop自带的Writable作为序列化工具的RPC引擎；
 *     ProtobufRpcEngine 用于描述使用protobuf作为序列化工具的RPC引擎。
 *
 * 之所以这样定义，是为了使Hadoop RPC可以方便地支持多种RPC序列化方式，同时在切换RPC引擎时并不需要更改源码，只需要通过配置更改RPC的RpcEngine对象即可。
 *
 * 我们知道ClientProtocol默认是使用ProtobufRpcEngine的，所以我们来看一下ProtobufRpcEngine.getProxy()方法的实现。
 */
@InterfaceStability.Evolving
public interface RpcEngine {

  /**
   * 客户端会调用RpcEngine.getProxy()方法获取一个本地接口的代理对象， 然后在这个代理对象上调用本地接口的方法。
   *
   * getProxy()方法的实现采用了 Java动态代理机制，
   * 客户端调用程序在代理对象上的调用会由一个 RpcInvocationHandler(java.lang.reflect.InvocationHandler的子类， 在RpcEngine的实现类中定义)对象处理，
   * 这个RpcInvocationHandler会将请求序列化  (使用 RpcEngine实现类定义的序列化方式) 并调用Client.call()方法将请求发送到远程服务器。
   * 当远程服务器发回响应信息后，RpcInvocationHandler会将响应信息反序列化并返回给调用程序，
   * 这一切通过Java动态代理机制对于调用程序是完全透明的，就像本地调用一样。
   */
  <T> ProtocolProxy<T> getProxy(Class<T> protocol,
                  long clientVersion, InetSocketAddress addr,
                  UserGroupInformation ticket, Configuration conf,
                  SocketFactory factory, int rpcTimeout,
                  RetryPolicy connectionRetryPolicy) throws IOException;

  /** Construct a client-side proxy object. */
  <T> ProtocolProxy<T> getProxy(Class<T> protocol,
                  long clientVersion, InetSocketAddress addr,
                  UserGroupInformation ticket, Configuration conf,
                  SocketFactory factory, int rpcTimeout,
                  RetryPolicy connectionRetryPolicy,
                  AtomicBoolean fallbackToSimpleAuth) throws IOException;

  /**
   * 该方法用于产生一个RPC Server对象，服务器会启动这个Server对象监听从客户端发来的请求。
   * 成功从网络接收请求数据后， Server对象会调用 Rpclnvoker(在RpcEngine的实现类中定义)对象处理这个请求。
   */
  RPC.Server getServer(Class<?> protocol, Object instance, String bindAddress,
                       int port, int numHandlers, int numReaders,
                       int queueSizePerHandler, boolean verbose,
                       Configuration conf, 
                       SecretManager<? extends TokenIdentifier> secretManager,
                       String portRangeConfig
                       ) throws IOException;

  /**
   * 根据给定的connection id 获取ProtocolMetaInfoPB 代理对象.
   */
  ProtocolProxy<ProtocolMetaInfoPB> getProtocolMetaInfoProxy(
      ConnectionId connId, Configuration conf, SocketFactory factory)
      throws IOException;
}
